// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: donggu

#include "coin2/exchange/base/feed_util/book_builder.h"

Transaction::Transaction(BookBuilder* book, bool enable_delta_book)
    : book_(book), enable_delta_book_(enable_delta_book) {
  book_->is_snapshot_ = false;
  if (enable_delta_book) {
    book_->delta_asks_.clear();
    book_->delta_bids_.clear();
  }
}

void Transaction::Clear() {
  book_->Clear();
  book_->delta_asks_.clear();
  book_->delta_bids_.clear();
  book_->is_snapshot_ = true;
}

void Transaction::BboClear() {
  book_->delta_asks_.clear();
  book_->delta_bids_.clear();
  book_->is_snapshot_ = true;
}

bool Transaction::IsCrossedBook() {
  if (min_ask_price_ && max_bid_price_) {
    if (min_ask_price_ < max_bid_price_) {
      return true;
    }
  }

  return false;
}

void Transaction::UpdateTimestamp(int64_t timestamp) {
  book_->timestamp_ = timestamp;
}

void Transaction::UpdateAsk(double price, double qty) {
  // TODO(jaewon): implement delta calc for snapshot
  if (enable_delta_book_ && !book_->is_snapshot_) {
    if (qty == 0) {
      if (book_->asks_.find(price) != book_->asks_.end()) {
        book_->delta_asks_.emplace_back(price, 0);
      }
    } else {
      if (book_->asks_.find(price) == book_->asks_.end() || book_->asks_.at(price) != qty) {
        book_->delta_asks_.emplace_back(price, qty);
      }
    }
  }

  if (qty == 0) {
    book_->asks_.erase(price);
  } else {
    book_->asks_[price] = qty;
    if (!min_ask_price_ || price < *min_ask_price_) {
      min_ask_price_.emplace(price);
    }
  }
}

void Transaction::UpdateBid(double price, double qty) {
  if (enable_delta_book_ && !book_->is_snapshot_) {
    if (qty == 0) {
      if (book_->bids_.find(price) != book_->bids_.end()) {
        book_->delta_bids_.emplace_back(price, 0);
      }
    } else {
      if (book_->bids_.find(price) == book_->bids_.end() || book_->bids_.at(price) != qty) {
        book_->delta_bids_.emplace_back(price, qty);
      }
    }
  }

  if (qty == 0) {
    book_->bids_.erase(price);
  } else {
    book_->bids_[price] = qty;
    if (!max_bid_price_ || price > *max_bid_price_) {
      max_bid_price_.emplace(price);
    }
  }
}

void Transaction::UpdateAsk0(double price, double qty) {
  if (enable_delta_book_ && !book_->is_snapshot_) {
    if (qty == 0) {
      if (book_->asks_.find(price) != book_->asks_.end()) {
        book_->delta_asks_.emplace_back(price, 0);
      }
    } else {
      if (book_->asks_.find(price) == book_->asks_.end() || book_->asks_.at(price) != qty) {
        book_->delta_asks_.emplace_back(price, qty);
      }
    }
  }

  while (!book_->asks_.empty() && book_->asks_.begin()->first < price) {
    if (enable_delta_book_ && !book_->is_snapshot_) {
      book_->delta_asks_.emplace_back(book_->asks_.begin()->first, 0);
    }
    book_->asks_.erase(book_->asks_.begin());
  }

  if (qty == 0) {
    book_->asks_.erase(price);
  } else {
    book_->asks_[price] = qty;
    if (!min_ask_price_ || price < *min_ask_price_) {
      min_ask_price_.emplace(price);
    }
  }
}

void Transaction::UpdateBid0(double price, double qty) {
  if (enable_delta_book_ && !book_->is_snapshot_) {
    if (qty == 0) {
      if (book_->bids_.find(price) != book_->bids_.end()) {
        book_->delta_bids_.emplace_back(price, 0);
      }
    } else {
      if (book_->bids_.find(price) == book_->bids_.end() || book_->bids_.at(price) != qty) {
        book_->delta_bids_.emplace_back(price, qty);
      }
    }
  }

  while (!book_->bids_.empty() && book_->bids_.rbegin()->first > price) {
    if (enable_delta_book_ && !book_->is_snapshot_) {
      book_->delta_bids_.emplace_back(book_->bids_.rbegin()->first, 0);
    }
    book_->bids_.erase(std::prev(book_->bids_.end()));
  }

  if (qty == 0) {
    book_->bids_.erase(price);
  } else {
    book_->bids_[price] = qty;
    if (!max_bid_price_ || price > *max_bid_price_) {
      max_bid_price_.emplace(price);
    }
  }
}

Transaction::~Transaction() { book_->CleanUp(min_ask_price_, max_bid_price_); }
